/*
 * User: ronma
 * Date: 21/02/2008
 *
 * Copyright (2005) IDI. All rights reserved.
 * This software is a proprietary information of Israeli Direct Insurance.
 * Created by IntelliJ IDEA.
 */
package com.idi.maven.plugins.poms;

import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.codehaus.plexus.util.FileUtils;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.jfrog.jade.plugins.common.injectable.MvnInjectableMojoSupport;
import org.jfrog.maven.annomojo.annotations.MojoExecute;
import org.jfrog.maven.annomojo.annotations.MojoGoal;
import org.jfrog.maven.annomojo.annotations.MojoParameter;
import org.jfrog.maven.annomojo.annotations.MojoPhase;
import org.tmatesoft.svn.core.SVNException;
import org.tmatesoft.svn.core.SVNURL;
import org.tmatesoft.svn.core.auth.BasicAuthenticationManager;
import org.tmatesoft.svn.core.auth.ISVNAuthenticationManager;
import org.tmatesoft.svn.core.auth.SVNAuthentication;
import org.tmatesoft.svn.core.auth.SVNPasswordAuthentication;
import org.tmatesoft.svn.core.internal.io.dav.DAVRepositoryFactory;
import org.tmatesoft.svn.core.wc.SVNClientManager;
import org.tmatesoft.svn.core.wc.SVNRevision;
import org.tmatesoft.svn.core.wc.SVNUpdateClient;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import static java.lang.String.format;
import static org.codehaus.plexus.util.StringUtils.isNotEmpty;
import static org.codehaus.plexus.util.StringUtils.join;

/**
 *
 */
@MojoGoal("devenv")
@MojoPhase("package")
@MojoExecute(phase = "package")
public class DevEnv extends MvnInjectableMojoSupport {

    @MojoParameter(/*required = true,*/expression = "${pompom}")
    private String pompom;

    private SVNUpdateClient client;
    private SVNClientManager manager;

    private static final File WORKSPACE_DIR = file(System.getenv("WORKSPACE_DIR"));

    private String svnRoot;
    //    private final String svnLocalJBossURL = "http://jdev/svn/config/idi-tools/1.0/JBoss/JBoss4.0.4.GA/localJboss";
    private final String svnLocalJBossURL = "http://jdev/svn/config/idi-tools/1.0/JBoss/JBoss4.2.2.GA/localJboss";

    public void execute() throws MojoExecutionException, MojoFailureException {
        assure(isNotEmpty(pompom), "please specify a pompom file (i.e. -Dpompom=bareket)");

        assure(file(WORKSPACE_DIR).exists(), format("WORKSPACE_DIR does not exist. please switch to the requested environment"));

        //get svn root
        svnRoot = getSvnRoot();
        getLog().info(format("svnRoot is set to %s", svnRoot));

        //update or checkout devenv module
        File devenvDir = file(WORKSPACE_DIR, "devenv");
        svnUpdateOrCheckout(devenvDir, svnRoot);

        //lookup for pompom
        File pompomDir = file(WORKSPACE_DIR, "devenv", "pompoms", pompom);
        File pompomFile = file(pompomDir, "pom.xml");
        assure(pompomFile.exists(), format("could not find pompom file for module [%s] at %s", pompom, pompomFile));

        //update or checkout pompom modules
        List<File> modules = parseModulesList(pompomFile);
        for (final File module : modules) {
            svnUpdateOrCheckout(module, svnRoot);
        }

        //mvn install devenv/setup
        mvn("install", file(WORKSPACE_DIR, "devenv", "setup", "pom.xml"));

        //mvn install pompom modules
        for (final File module : modules) {
            mvn("install", file(module, "pom.xml"));
        }

        // build idea project
        mvn("jade-idea:idea", pompomFile);

        // deploy to local jboss
        File jbossDir = file(WORKSPACE_DIR, "jboss");
        svnUpdateOrCheckout(jbossDir, svnLocalJBossURL, true);

        //shutdown jboss
        exec(path(WORKSPACE_DIR.getPath(), "jboss", "bin", "shutdown.bat") + " -S", false, false);

        //local deploy idi.ear
        exec("cmd /c " + path(WORKSPACE_DIR.getPath(), "devenv", "build", "deployables", "idi.ear", "cleanDeploy.bat"), true, true);
    }

    private String getSvnRoot() throws MojoExecutionException {
        if (svnRoot == null) {
            try {

                SVNURL svnurl = getSvnClientManager().getStatusClient().doStatus(getProject().getFile(), true).getURL();
                String pomRelativePath = relativePath(WORKSPACE_DIR, getProject().getFile());
                String path = svnurl.getPath().substring(0, svnurl.getPath().indexOf(pomRelativePath));
                svnRoot = String.format("%s://%s%s", svnurl.getProtocol(), svnurl.getHost(), path);
            } catch (SVNException e) {
                throw new MojoExecutionException(format("svn exception occured while trying to get the svn root"), e);
            }
        }
        return svnRoot;
    }

    private void mvn(String goal, File pom) throws MojoExecutionException {
        //todo mamo: this is sooooo ugly, but I couldn't get maven to run another plugin from within itself, nor to get the errorlevel return from the 'cmd /c some.bat'
        String command = "cmd /c mvn " + goal + " -f \"" + pom.getAbsolutePath() + "\"";
        boolean error = false;
        try {
            getLog().info(format("mvn %s -f %s", goal, pom.getCanonicalPath()));
            Process process = Runtime.getRuntime().exec(command);

            BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
            String line;
            StringBuilder sb = new StringBuilder(1024);
            while ((line = reader.readLine()) != null) {
                sb.append(line).append("\n");
//                System.out.println("\t" + line);
                if (line.startsWith("[ERROR]")) {
                    error = true;
                } //todo mamo: omg I can't believe I wrote that shit
            }

            process.waitFor();

            if (!error) {
//                getLog().info(format("BUILD SUCCESSFUL [%s]", command));
            } else {
                getLog().error("BUILD FAILURE\n" + sb.toString());
                throw new MojoExecutionException(format("failed executing %s", command));
            }

        } catch (Exception e) {
            throw new MojoExecutionException(format("exception while executing command %s", command), e);
        }
    }

    private void exec(final String command, boolean readLine, boolean waitFor) throws MojoExecutionException {
        try {
            getLog().info(command);
            Process process = Runtime.getRuntime().exec(command);

            if (readLine) {
                BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
                while (reader.readLine() != null) {
                } //System.out.println("\t" + line);
            }

            if (waitFor) {
                process.waitFor();
            }

        } catch (Exception e) {
            throw new MojoExecutionException(format("exception while executing command %s", command), e);
        }
    }

//    /**
//
//     @param command
//     @param optional : 2 length optional vararg for -
//            #1: dontProcessInputStream
//            #2: dontWaitforProcess
//     @throws MojoExecutionException
//     */
//    private void executeShellCommand(final String command, boolean... optional) throws MojoExecutionException {
//        getLog().info(format("%s", command));
//        try {
//            Process process = Runtime.getRuntime().exec(command);
//
//            boolean dontProcessInputStream = optional.length != 0 && optional[0];
//            if (!dontProcessInputStream) {
//                BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
//                String line;
//                while ((line = reader.readLine()) != null) {
//                    System.out.println("\t" + line);
//                }
//            }
//
//            boolean dontWaitforProcess = optional.length >= 1 && optional[1];
//            if (!dontWaitforProcess) {
//                int exitValue = process.waitFor();
//                if (exitValue != 0) {
//                    throw new MojoExecutionException(format("exit value %d for %s", exitValue, command));
//                }
//            }
//
////            getLog().info(format("succefully executed %s", command));
//
//        } catch (Exception e) {
//            throw new MojoExecutionException(format("exception while executing command %s", command), e);
//        }
//    }

    private static File file(File aFile, String... paths) {
        return new File(aFile, path(paths));
    }

    public static File file(String... paths) {
        return new File(path(paths));
    }

    private static String path(String... paths) {
        return join(paths, File.separator);
    }

    private static String normalize(String path) {
        String result;

        result = path.replaceAll("\\\\", "/");

        return result;
    }

    private static List<File> parseModulesList(final File pom) {
        List<File> modules = new ArrayList<File>(10);
        try {
            Document document = new SAXReader().read(pom);
            Iterator<Element> iterator = document.getRootElement().element("modules").elementIterator("module");
            while (iterator.hasNext()) {
                Element element = iterator.next();
                modules.add(file(pom.getParent(), element.getText()));
            }
        } catch (Exception e) {
            throw new RuntimeException(format("error while parsing %s", pom.getAbsolutePath()));
        }
        return modules;
    }

    public SVNUpdateClient getSVNUpdateClient() {
        if (client == null) {
            final SVNClientManager manager = getSvnClientManager();
            client = manager.getUpdateClient();
        }

        return client;
    }

    private SVNClientManager getSvnClientManager() {
        if (manager == null) {
            DAVRepositoryFactory.setup();
            //todo mamo: get real username and password
            ISVNAuthenticationManager authManager = new BasicAuthenticationManager(
                    new SVNAuthentication[]{new SVNPasswordAuthentication("query", "query", false/*do NOT store authentication*/)});
            manager = SVNClientManager.newInstance(null, authManager);
        }
        return manager;
    }

    private void svnUpdateOrCheckout(File moduleDir, String svnRoot, boolean... optional) throws MojoExecutionException {
        try {
            String moduleCanonicalPath = relativePath(WORKSPACE_DIR, moduleDir);

            boolean dontUseRelativeModulePath = optional.length != 0 && optional[0];
            String uri = dontUseRelativeModulePath ? svnRoot : svnRoot + moduleCanonicalPath;
            SVNURL expectedSvnUrl = SVNURL.parseURIDecoded(uri);

            long revision;
            if (moduleDir.exists()) {
                //is it the expected working copy?
                SVNURL foundSvnUrl = getSvnClientManager().getStatusClient().doStatus(moduleDir, true).getURL();

                if (foundSvnUrl.equals(expectedSvnUrl)) {
                    revision = doUpdate(moduleDir, svnRoot, moduleCanonicalPath);
                } else {
                    //in this case, the foundSvnUrl is not the expected svn working copy
                    getLog().info(format("existing module %s svn entry points to %s and not to %s, as expected", moduleDir, foundSvnUrl.toString(), expectedSvnUrl.toString()));
                    getLog().info(format("deleting directory %s", moduleDir));
                    FileUtils.deleteDirectory(moduleDir);
                    revision = doCheckout(moduleDir, moduleCanonicalPath, expectedSvnUrl);
                }
            } else {
                revision = doCheckout(moduleDir, moduleCanonicalPath, expectedSvnUrl);
            }

//            getLog().info(format("module %s updated to revision %d", moduleCanonicalPath, revision));
        } catch (SVNException e) {
            throw new MojoExecutionException(format("failed to execute svn operation on module %s", moduleDir.getAbsolutePath()), e);
        } catch (IOException e) {
            throw new RuntimeException(e); //todo mamo: better log
        }
    }

    private String relativePath(File baseDir, File file) {
        try {
            String basePath = baseDir.getPath();
            if (!basePath.endsWith("\\")) {
                basePath = basePath + "\\";
            }

            String moduleCanonicalPath = file.getCanonicalPath().substring(basePath.length());

            return normalize(moduleCanonicalPath);

        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    private long doUpdate(File moduleDir, String svnRoot, String moduleCanonicalPath) throws SVNException {
        getLog().info(format("svn update %s ... [%s/%s]", moduleCanonicalPath, svnRoot, moduleCanonicalPath));
        return getSVNUpdateClient().doUpdate(moduleDir, SVNRevision.HEAD, true);
    }

    private long doCheckout(File moduleDir, String moduleCanonicalPath, SVNURL expectedSvnUrl) throws SVNException {
        getLog().info(format("svn checkout %s ... [%s]", moduleCanonicalPath, expectedSvnUrl));
        return getSVNUpdateClient().doCheckout(expectedSvnUrl, moduleDir, SVNRevision.HEAD, SVNRevision.HEAD, true/*recursive*/);
    }

    private static void assure(boolean condition, String errorMsg) throws MojoExecutionException {
        if (!condition) {
            throw new MojoExecutionException(errorMsg);
        }
    }

    //    private void mvn2(String goal, File pom) throws MojoExecutionException, MavenEmbedderException {
//        MavenEmbedder embedder = new MavenEmbedder();
//
//embedder.start();
//
//Settings settings = embedder.buildSettings(
//        embedder.getUserSettingsPath( null ),
//        embedder.getGlobalSettingsPath(),
//        false,
//        false,
//        false,
//        false );
//
//Properties properties = new Properties();
//// set archetype parameters in properties
//
//MavenExecutionRequest request = new DefaultMavenExecutionRequest()
//        .setBasedir("")
//        .setGoals( Arrays.asList( new String[] { "archetype.create" } )
//        .setLocalRepositoryPath( embedder.getLocalRepositoryPath( settings )
//        .setSettings( settings )
//        .setProperties( properties );
//
//embedder.execute( request );
//
//
//
//        File projectDirectory = pom.getParentFile();
//
//        File user = new File( projectDirectory, "settings.xml" );
//
////        Configuration configuration = new DefaultConfiguration()
////            .setUserSettingsFile( user )
////            .setClassLoader( Thread.currentThread().getContextClassLoader() );
////
////        ConfigurationValidationResult validationResult = MavenEmbedder.validateConfiguration( configuration );
//
////        if ( validationResult.isValid() )
////        {
//            MavenEmbedder embedder = new MavenEmbedder().;
//            MavenExecutionRequest request = new DefaultMavenExecutionRequest()
//                .setBaseDirectory( projectDirectory )
//                .setGoals( Arrays.asList( new String[]{"clean", "install"} ) );
//
//            MavenExecutionResult result = embedder.execute( request );
//        }
}